﻿
Imports std

Namespace managedServer
    Namespace configNaming
        Namespace ProHttpS
            Public Module ProHttpS
                Public Const name As String = "ProHttpS"
                Public Const ports As String = "ports"
                Public Const app As String = "app"
                Public Const [namespace] As String = "namespace"
                Public Const [class] As String = "class"
                Public Const threadcount As String = "threadcount"
                Public Const csscrunch As String = "csscrunch"
                Public Const enableHDDoSLite As String = "enableHDDoSLite"
            End Module
        End Namespace

        Namespace defaultValue
            Namespace ProHttpS
                Public Module ProHttpS
                    Public Const ports As String = "80,81"
                    Public Const threadcount As Int64 = 16
                    Public Const enableHDDoSLite As Boolean = False
                End Module
            End Namespace
        End Namespace
    End Namespace

    Public Module ProHttpS
        Public Event initial(ByVal config As configMonitor)
        Public Event configChanged(ByVal config As configMonitor)
        Public Event serviceshutdown()
        Public defaultConfigfile As String = "ProHttpS.ini"
        Private WithEvents config As configMonitor = Nothing
        Private WithEvents server As processorHttpServer = Nothing
        Private _managedRequestProcessors As vector(Of requestProcessor) = Nothing
        Private section As String = Nothing

        'it's by design, if an application is managed by ProHttpS, it should have only one http server
        Public Function processorHttpServer() As processorHttpServer
            Return server
        End Function

        Public Function managedRequestProcessors() As vector(Of requestProcessor)
            assert(Not _managedRequestProcessors Is Nothing, "_managedRequestProcessors has not been initialized.")
            Return _managedRequestProcessors
        End Function

        Public Function insertRequestProcessor(ByVal p As requestProcessor) As vector(Of requestProcessor).iterator
            Return managedRequestProcessors().insert(p)
        End Function

        Private Sub startservice(ByVal contextParameter As httpContext.parameterDefinition)
            assert(Not config Is Nothing, "have not opened configuration")
            If server Is Nothing Then
                server = New processorHttpServer(contextParameter)
            Else
                server.processors().clear()
            End If
            Dim ports As String = Nothing
            ports = config.parameter(section, configNaming.ProHttpS.ports,
                                     configNaming.defaultValue.ProHttpS.ports)
            server.addports(ports)

            If config.parameter(section, configNaming.ProHttpS.enableHDDoSLite,
                                configNaming.defaultValue.ProHttpS.enableHDDoSLite) Then
                server.enableHDDoSLite(config, section)
            End If

            server.threadCount = config.parameter(section, configNaming.ProHttpS.threadcount,
                                                  configNaming.defaultValue.ProHttpS.threadcount)

            server.addRequestProcessor(New cssrequest(section, configNaming.ProHttpS.csscrunch), _
                                       config)
            server.addRequestProcessor(New staticrequest(), config)
            server.addRequestProcessor(New buildInfoPage(), config)
            server.addRequestProcessor(New verpage(), config)
            server.addRequestProcessor(New favicon(), config)
            server.addRequestProcessor(New diagnostic(), config)
            server.addRequestProcessor(New status(), config)
            server.addRequestProcessor(New pingProcessor(), config)

            For i As Int64 = 0 To managedRequestProcessors().size() - 1
                server.addRequestProcessor(managedRequestProcessors(i), config)
            Next

            raiseError("http server will start on ports " + ports + _
                       " with " + Convert.ToString(server.threadCount) + " threads, " + _
                       ifoper(server.hddosliteEnabled(), "enabled", "disabled") + " hddoslite")
            server.start()
        End Sub

        Private Sub restartservice()
#If RETIRED Then
            If Not server Is Nothing Then
                Dim i As Int64
                For i = 0 To server.processors().size() - 1
                    server.processors(i).initial(config)
                Next
            End If
#End If
            'suppose there should be no possibility to restartservice before startservice,
            'so the server has already been initialized with context parameter, do not take care of it here
            startservice(Nothing)
        End Sub

        Private Sub stopservice()
            If Not server Is Nothing Then
                server.stop()
            End If
        End Sub

        Private Sub shutdown()
            RaiseEvent serviceshutdown()
            stopservice()
            finishmain()
        End Sub

        Private Sub monitor()
            While Not consoleExtSignal() AndAlso (server.health() OrElse config.refreshing())
                sleep()
            End While

            If consoleExtSignal() Then
                raiseError("caught console ext signal, shutdown...")
            ElseIf Not server.health() Then
                raiseError("caught httpserver error.", errorHandle.errorType.critical)
            Else
                raiseError("unhandled error.", errorHandle.errorType.critical)
            End If
        End Sub

        Private Sub unhandledException(ByVal ex As Exception)
            shutdown()
        End Sub

        Public Sub startServer(ByVal args() As String, _
                               Optional ByVal sectionName As String = configNaming.ProHttpS.name, _
                               Optional ByVal contextParameter As httpContext.parameterDefinition = Nothing)
            copy(section, sectionName)
            dpslib.consoleApplicationStartParameters.unhandledExceptionhandler = AddressOf unhandledException
            dpslib.startmain()
            Dim configfile As String = Nothing
            If args.Length() > 0 Then
                configfile = args(0)
            Else
                configfile = defaultConfigfile
            End If
            config = New configMonitor()
            assert(config.open(configfile), "cannot open configuration file " + configfile)

            RaiseEvent initial(config)
            startservice(contextParameter)
            monitor()
            shutdown()
        End Sub

        Private Sub config_ConfigChanged() Handles config.ConfigChanged
            If Not server Is Nothing Then
                raiseError("caught configuration changed, restart service.")
                RaiseEvent ConfigChanged(config)
                restartservice()
            End If
        End Sub

        Private Sub server_receive(ByVal context As httpContext) Handles server.receive
            assert(False, "server.receive event should not be called in processorHttpServer")
        End Sub

        Sub New()
            _managedRequestProcessors = New vector(Of requestProcessor)
        End Sub
    End Module
End Namespace
